package questionary;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.LinkedList;
import java.util.Random;

import trivia.ErrorFrame;

import myExceptions.MyFileException;


/**
 * Un cuestionario que se obtiene apartir de un archivo especificado previamente.
 * Contiene las preguntas de los 3 tipos de dificultades 
 *
 */
public class Questionary {
	static private int countQ = 10;
	private LinkedList <Question> easy;
	private LinkedList <Question> normal;
	private LinkedList <Question> hard;
	private LinkedList <Question> play;
	private int totalScore = 0;
	private int totalQcount;
	private int difChose;
	
	private Object [][] allQuestions; 
	
	private int type;
	private int diffic;
	private int optionsCount;
	private int correctOptionsCount;
	private int score;
	
	private String question;
	private String options [];
	private String answers [];
	
	/**
	 * Crea un cuestionario con todas las preguntas leidas de un archivo
	 * @param buf un bufer que apunta al archivo previamente elegido
	 * @param difficulty dificultad elegida por el usuario al comenzar el juego
	 * @throws MyFileException lanza una excepcion definida por el usuario cuando hay un
	 * error en el archivo. 
	 */
	public Questionary (BufferedReader buf, String difficulty) throws MyFileException {
		
		Question q = null;
		easy = new LinkedList<Question>();
		normal = new LinkedList<Question>();
		hard = new LinkedList<Question>();
		play = new LinkedList<Question>();
		
		while(getQuestionsFromFile(buf) != -1) {
			switch (type) {
			case 1:
				q = new SimpleQ(question, answers[0], diffic, score);
				break;
			case 2:
				q = new NumberQ(question, answers[0], diffic, score);
				break;
			case 3:
				q = new MultOneAnsQ(question, answers[0], options, diffic, score);
				break;
			case 4:
				q = new MultMultAnsQ(question, answers, options, diffic, score);
				break;
			case 5:
				q = new TrueFalseQ(question, answers[0], diffic, score);
			}
			
			switch (diffic) {
			case 0:
				easy.add(q);
				break;
			case 1:
				normal.add(q);
				break;
			case 2:
				hard.add(q);
				break;
			default:
				//TODO debug
		
				System.exit(1);
			}
		}
		
		setRandomQ(difficulty);
				
		if (play.size() < countQ) {
			throw new MyFileException ("Error in your questionary file.");
		}
		
		totalQcount = easy.size() + normal.size() + hard.size();
		allQuestions = getAllQuestions();
	
	}
	/**
	 * metodo privado que es utilizado por el constructor de la clase para leer los datos del archivo
	 * para formar el cuestionario.
	 * @param buf un bufer que apunta al archivo previamente elegido.
	 * @return -1 si termino de leer el archivo, caso contrario 1.
	 * @throws MyFileException si el archivo contiene datos incorrectos
	 * @see Questionary
	 */
	private int getQuestionsFromFile(BufferedReader buf) throws MyFileException {
				
		String line;
		
		line = readLine(buf);
		
		if(line == null)
			return -1;
		
		try {
			type = Integer.parseInt(line);
		} catch (NumberFormatException e) {
			System.out.println("tipo de pregunta --> incorrecto");
			throw new MyFileException("Corrupt file: type incorrect");
		}
		
		line = readLine(buf);
		try {
			diffic = Integer.parseInt(line);
		} catch (NumberFormatException e) {
			System.out.println("dificultad --> incorrecto");
			throw new MyFileException("Corrupt file");
		}
		
		line = readLine(buf);
		try {
			optionsCount = Integer.parseInt(line);
		} catch (NumberFormatException e) {
			System.out.println("cantidad de opciones --> incorrecto");
			throw new MyFileException("Corrupt file");
		}
		
		line = readLine(buf);
		try {
			correctOptionsCount = Integer.parseInt(line);
		} catch (NumberFormatException e) {
			System.out.println("cantidad de opciones correcta --> incorrecto");
			throw new MyFileException("Corrupt file");
		}
		
		question = readLine(buf);
		
		options = new String [optionsCount];
		for (int i=0 ; i < optionsCount; i++) {
			options [i] = readLine(buf);
		}
		
		answers = new String [correctOptionsCount];
		for (int i=0; i < correctOptionsCount ; i++) {
			answers [i] = readLine(buf);
		}
		
		line = readLine(buf);
		try {
			score = Integer.parseInt(line);
		} catch (NumberFormatException e) {
			System.out.println("puntos --> incorrecto    " + line);
			throw new MyFileException("Corrupt file");
		}
	
		if((line = readLine(buf)) == null)
			return -1;
				
		if (!line.equals("####") && line != null) {
			throw new MyFileException("Corrupt file");
		}
		
		return 1;		
	}


	/**
	 * Lee una linea del archivo de preguntas
	 * @param buf 
	 * @return una linea de string
	 * @throws MyFileException
	 */
	private String readLine (BufferedReader buf) throws MyFileException {
		
		String line = null;

		try {
			line = buf.readLine();
		} catch (IOException e) {
			throw new MyFileException("Corrupt file");
		} finally {
		}
		
		return line;
	}
	
	public void setRandomQ (String difficulty) {
		if (difficulty.equals("Easy")) {
			difChose = 0;
			try {
				play = randomQ(easy);
			} catch (MyFileException e) {
				new ErrorFrame(e.getMessage());				
			}
		} else if (difficulty.equals("Normal")) {
			difChose = 1;
			try {
				play = randomQ(normal);
			} catch (MyFileException e) {
				new ErrorFrame(e.getMessage());
			}
		} else {
			difChose = 2;
			try{
				play = randomQ(hard);
			} catch (MyFileException e) {
				new ErrorFrame(e.getMessage());
			}
		}
	}
	/**
	 * Crea un array[2][] de clase Object donde array[0] contiene un resumen de todas las preguntas
	 * existentes. array[1] contiene todas las preguntas de tipo Question. El resumen es usado para
	 * mostrarle al usuario cuando quiere modificar alguna pregunta.
	 * Un resumen consiste la pregunta acotado en 40 caracteres, su tipo y el puntaje.
	 * @return todas las preguntas existentes del questionario
	 */
	public Object[][] getAllQuestions() {
		
		LinkedList <String> ret = new LinkedList <String> ();
		LinkedList <Question> retQ = new LinkedList <Question> ();
		
		String aux;
		int i, n, end, limit = 40;
		
		allQuestions = new Object [2][];
		allQuestions [0] = new Object [totalQcount];
		allQuestions [1] = new Object [totalQcount];
		
		end = limit;
		
		if ((n = easy.size()) > 0) {
			for (i = 0; i < n; i++, end = limit) {
				aux = easy.get(i).getQuestion();
				retQ.add(easy.get(i));
				if (aux.length() < limit) {
					end = aux.length();
				}
				aux = aux.substring(0, end);
				ret.add(aux + " --- " + "TYPE: " + easy.get(i).getType() + " --- " + "SCORE: " + easy.get(i).getScore());
			}		
		}
		
		if ((n = normal.size()) > 0) {
			for (i = 0; i < n; i++, end = limit) {
				aux = normal.get(i).getQuestion();
				retQ.add(normal.get(i));
				if (aux.length() < limit) {
					end = aux.length();
					
				}
				aux = aux.substring(0, end);
				ret.add(aux + " --- " + "TYPE: " + normal.get(i).getType() + " --- " + "SCORE: " + normal.get(i).getScore());
			}
		}
		
		if((n = hard.size())> 0) {
			for (i = 0; i < n; i++, end = limit) {
				aux = hard.get(i).getQuestion();
				retQ.add(hard.get(i));
				if (aux.length() < limit) {
					end = aux.length();
				}
				aux = aux.substring(0, end);
				ret.add(aux + " --- " + "SCORE: " + hard.get(i).getType() + " --- " + "SCORE: " + hard.get(i).getScore());
			}
		}
		
		allQuestions[0] = ret.toArray();
		allQuestions[1] = retQ.toArray();
		
		return allQuestions;
	}
	/**
	 * Devuelve un array donde contiene un resumen de todas las preguntas existentes del
	 * cuestionario leido del archivo.
	 * @return resumen de todas las preguntas existentes.
	 */
	public Object [] getAllQsummary () {
		totalQcount = easy.size() + normal.size() + hard.size();
		allQuestions = getAllQuestions();
		return allQuestions[0];
	}
	/**
	 * 
	 * @param index
	 * @return Devuelve una pregunta de tipo Question con indice igual a index
	 */
	public Question getEditQ (int index) {
		return (Question)allQuestions[1][index];
	}
	
	//TODO debug
	/*
	public void showPlayQ () {
	
		for (int i = 0; i < play.size(); i++) {
			System.out.println(play.get(i).getQuestion());
		}
	}
	*/
	/**
	 * @return devuelve la dificultad elegida
	 */
	public int getDifficulty () {
		return difChose;
	}
	
	/**
	 * Genera la lista aleatoriade preguntas que se le preguntaran al usuario 
	 * @param l lista de 10 preguntas
	 * @return
	 * @throws MyFileException 
	 */
	private LinkedList <Question> randomQ (LinkedList<Question> l) throws MyFileException {
		
		LinkedList <Question> ret = new LinkedList <Question> ();
		Random generator = new Random();
		int index;
		
		if (l.size() < countQ) {
			throw new MyFileException("Difficulty not available");
		}
		
		try {
			for (int i=countQ; i > 0; i--) {
				index = Math.abs(generator.nextInt() % i);				
				ret.add(l.get(index));
				totalScore += (l.get(index)).getScore();
				l.addLast(l.get(index));
				l.remove(index);
			}
			
			//TODO debug
			System.out.println("totalScore: "+totalScore);
			
			return ret;
		} catch (IndexOutOfBoundsException e) {
			throw new MyFileException("Error in file");
		}
		
	}
	
	/**
	 * Guarda en el archivo el cuestionario.
	 * @param buf buffer que apunta al archivo destino.
	 * @throws MyFileException si ocurre un error cuando se intenta escribir al archivo.
	 */
	public void saveFile(BufferedWriter buf) throws MyFileException {
		try {
			for (int i = 0; i < easy.size(); i++) {
				buf.write(Integer.toString(easy.get(i).getType())+'\n'); //tipo
				buf.write("0"+'\n'); //dificultad
				buf.write(Integer.toString(easy.get(i).getOptionsCount())+'\n');
				buf.write(Integer.toString(easy.get(i).getCorrectOptionsCount())+'\n');
				buf.write(easy.get(i).getQuestion()+'\n');
				
				//ahora se escribe todas las opciones
				for (int j = 0; j < easy.get(i).getOptionsCount(); j++) {
					buf.write(easy.get(i).getOptions()[j]+'\n');
				}
				
				//ahora se escribe todas las respuestas
				for (int j = 0; j < easy.get(i).getCorrectOptionsCount(); j++) {
					buf.write(easy.get(i).getAnswers()[j]+'\n');
				}
				
				buf.write(Integer.toString(easy.get(i).getScore())+'\n');
				buf.write("####"+'\n');
			}
			
			for (int i = 0; i < normal.size(); i++) {
				buf.write(Integer.toString(normal.get(i).getType())+'\n'); //tipo
				buf.write("1"+'\n'); //dificultad
				buf.write(Integer.toString(normal.get(i).getOptionsCount())+'\n');
				buf.write(Integer.toString(normal.get(i).getCorrectOptionsCount())+'\n');
				buf.write(normal.get(i).getQuestion()+'\n');
				
				//ahora se escribe todas las opciones
				for (int j = 0; j < normal.get(i).getOptionsCount(); j++) {
					buf.write(normal.get(i).getOptions()[j]+'\n');
				}
				
				//ahora se escribe todas las respuestas
				for (int j = 0; j < normal.get(i).getCorrectOptionsCount(); j++) {
					buf.write(normal.get(i).getAnswers()[j]+'\n');
				}
				
				buf.write(Integer.toString(normal.get(i).getScore())+'\n');
				buf.write("####"+'\n');
			}
			
			for (int i = 0; i < hard.size(); i++) {
				buf.write(Integer.toString(hard.get(i).getType())+'\n'); //tipo
				buf.write("2"+'\n'); //dificultad
				buf.write(Integer.toString(hard.get(i).getOptionsCount())+'\n');
				buf.write(Integer.toString(hard.get(i).getCorrectOptionsCount())+'\n');
				buf.write(hard.get(i).getQuestion()+'\n');
				
				//ahora se escribe todas las opciones
				for (int j = 0; j < hard.get(i).getOptionsCount(); j++) {
					buf.write(hard.get(i).getOptions()[j]+'\n');
				}
				
				//ahora se escribe todas las respuestas
				for (int j = 0; j < hard.get(i).getCorrectOptionsCount(); j++) {
					buf.write(hard.get(i).getAnswers()[j]+'\n');
				}
				
				buf.write(Integer.toString(hard.get(i).getScore())+'\n');
				buf.write("####"+'\n');
			}
		} catch (IOException e) {
			throw new MyFileException("Error saving file");
		}
	}
	/**
	 * 
	 * @param index indice de la lista de las preguntas
	 * @return devuelve el tipo de pregunta
	 */
	public int getType(int index) {
		return play.get(index).getType();
	}
	
	/**
	 * 
	 * @param index indice de la lista de las preguntas
	 * @return devuelve la cantidad de opciones de la pregunta
	 */
	public int getOptionsCount(int index) {
		return play.get(index).getOptionsCount();
	}
	
	/**
	 * 
	 * @param index indice de la lista de las preguntas
	 * @return devuelve las opciones que se puede elegir de una pregunta
	 */
	public String[] getOptions(int index) {
		return play.get(index).getOptions();
	}

	/**
	 * 
	 * @param index indice de la lista de las preguntas
	 * @return devuelve la pregunta
	 */
	public String getQuestion(int index) {
		return play.get(index).getQuestion();
	}
	
	/**
	 * 
	 * @param index indice de la lista de las preguntas
	 * @return devuelve el puntaje de la pregunta
	 */
	public int getScores(int index) {
		// TODO Auto-generated method stub
		return play.get(index).getScore();
	}
	
	/**
	 * Verifica si es correcta la contestacion de la pregunta
	 * @param index el indice de la lista de las preguntas
	 * @param s la contestacion de la pregunta (para el caso de la pregunta tipo 4, puede ser varias)
	 * @param answsize size de s
	 * @return true si se contesto correctamente
	 */
	public boolean isCorrect(int index, String[] s, int answsize) {
		return play.get(index).isCorrect(s, answsize);
	}
	/**
	 * Verifica si es correcta la contestacion de la pregunta
	 * @param index el indice de la lista de las preguntas
	 * @param s la contestacion de la pregunta 
	 * @return true si se contesto correctamente
	 */
	public boolean isCorrect(int index, String s) {
		return  play.get(index).isCorrect(s);
	}
	/**
	 * 
	 * @return la cantidad de preguntas del juego
	 */
	public int getCountQ() {
		return countQ;
	}
	
	/**
	 * 
	 * @return el puntaje que total que puede obtener el usuario en caso
	 * de contestar correctamente todas las preguntas
	 */
	public double getTotalScore() {
		return totalScore;
	}
	/**
	 * Agrega a la lista la
	 * @param singleQ
	 */
	public void addQuestion(Question singleQ) {
		switch (diffic) {
		case 0:
			easy.add(singleQ);
			break;
		case 1:
			normal.add(singleQ);
			break;
		case 2:
			hard.add(singleQ);
			break;
		default:
			//TODO debug
	
			System.exit(1);
		}
		
	}
	
	/**
	 * setea el puntaje que puede llegar a obtener el usuario
	 * @param totalScore
	 */
	public void setTotalScore(int totalScore) {
		this.totalScore = totalScore;
	}


}
